#include "../include/process_pool.h"
#include "../include/child_func.h"

// 创建子进程
int make_child(process_data_t *p_manage, int process_num)
{
    for (int i = 0; i < process_num; i ++)
    {
        int fds[2]; // 通信的管道
        socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
        
        pid_t pid = fork(); // 子进程pid
        if (pid == 0)
        {
            close(fds[1]);
            child_handle(fds[0]); // 子进程使用fds[0]和父进程通信
        }
        close(fds[0]);
        // 初始化子进程信息
        p_manage[i].pid = pid;
        p_manage[i].fd = fds[1]; // 父进程使用fds[1]和子进程通信
        p_manage[i].busy = 0;
#ifdef DEBUG
        printf("p_manage[%d].fd=%d\n",i,p_manage[i].fd);
#endif
    }
}

// 子进程任务
int child_handle(int fd)
{
    int new_fd; // 任务id
    char flag = 1;
    while (1)
    {
        // read(fd, &new_fd, sizeof(new_fd)); // 读管道
        int exit_flag;
        recv_fd(fd, &new_fd, &exit_flag);
        if (exit_flag)
        {
            printf("child %d will exit\n", getpid());
            exit(0);
        }
        printf("get client task %d\n", new_fd);
        reception(new_fd);
        printf("client task %d leaved!\n", new_fd);
        write(fd, &flag, sizeof(flag));// 通知父进程完成任务
        close(new_fd); // 在子进程中close，只会使new_fd的引用计数 -1
    }
}

int tcp_init(int* socket_fd,char* ip,char* port)
{
    // socket
    int sfd;
    sfd=socket(AF_INET,SOCK_STREAM,0);//初始化一个网络描述符，对应了一个缓冲区
    ERROR_CHECK(sfd,-1,"socket");

    //在bind之前去执行setsockopt,设定端口重用
    int reuse=1,ret;
    ret=setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int));
    ERROR_CHECK(ret,-1,"setsockopt");
    struct sockaddr_in ser_addr;
    bzero(&ser_addr,sizeof(ser_addr));//清空
    ser_addr.sin_family=AF_INET;//代表要进行ipv4通信
    ser_addr.sin_addr.s_addr=inet_addr(ip);//把ip的点分十进制转为网络字节序
    ser_addr.sin_port=htons(atoi(port));//把端口转为网络字节序
    
    // bind
    ret=bind(sfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
    ERROR_CHECK(ret,-1,"bind");
    
    //listen 开始监听,端口就开启了
    ret=listen(sfd,10);
    ERROR_CHECK(ret,-1,"listen");
    *socket_fd = sfd;//socket_fd是一个传出参数
    return 0;
}

int epoll_add(int epfd,int fd)
{
    struct epoll_event event;
    event.events = EPOLLIN; //读事件
    event.data.fd = fd;
    int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event);
    ERROR_CHECK(ret,-1,"epoll_ctl");
    return 0;
}

// 发送文件描述符
int send_fd(int sfd, int fd, int exit_flag)
{
    // 准备要发送的信息
    struct msghdr msg;
    bzero(&msg,sizeof(msg)); // 把结构体清空
    struct iovec iov[1]; // 用户态要传递一些内容
    iov[0].iov_base=&exit_flag;
    iov[0].iov_len=sizeof(exit_flag);
    msg.msg_iov=iov;
    msg.msg_iovlen=1;

    // 准备变长结构体
    int cmsg_len=CMSG_LEN(sizeof(int));
    struct cmsghdr *cmsg=(struct cmsghdr *)malloc(cmsg_len);
    cmsg->cmsg_len=cmsg_len;
    cmsg->cmsg_level=SOL_SOCKET;
    cmsg->cmsg_type=SCM_RIGHTS;
    *(int*)CMSG_DATA(cmsg)=fd; // 给变长结构体的最后一个成员赋值
    msg.msg_control=cmsg;
    msg.msg_controllen=cmsg_len;

    int ret;
    ret = sendmsg(sfd,&msg,0);
    ERROR_CHECK(ret,-1,"sendmsg");
    return 0;
}

// 接收文件描述符
int recv_fd(int sfd,int *fd, int *exit_flag)
{
    struct msghdr msg;
    bzero(&msg,sizeof(msg)); // 把结构体清空
    struct iovec iov[1]; // 用户态要传递一些内容
    iov[0].iov_base=exit_flag;
    iov[0].iov_len=sizeof(exit_flag);
    msg.msg_iov=iov;
    msg.msg_iovlen=1;

    // 构造变长结构体
    int cmsg_len=CMSG_LEN(sizeof(int));
    struct cmsghdr *cmsg=(struct cmsghdr *)malloc(cmsg_len);
    cmsg->cmsg_len=cmsg_len;
    cmsg->cmsg_level=SOL_SOCKET;
    cmsg->cmsg_type=SCM_RIGHTS;
    msg.msg_control=cmsg;
    msg.msg_controllen=cmsg_len;
    
    int ret;
    ret=recvmsg(sfd,&msg,0);
    ERROR_CHECK(ret,-1,"recvmsg");
    *fd=*(int*)CMSG_DATA(cmsg);
    return 0;
}

